/* use symbol tables / scopes to resolve names */

package treecalc.comp.java; 

import java.io.IOException;
import java.io.PrintStream;
import java.util.List;

import treecalc.comp.GenInput;
import treecalc.comp.ModelSimple;
import treecalc.comp.ScopeNode;
import treecalc.comp.ModelSimple.NodeData;

import treecalc.rt.S.Traceaction;


public class JavaDiv {
	public final static boolean DEBUGOUT = false;
	private final ModelSimple model;
	private final String foldernameout;
	private final String packagename;
	private final boolean gwt;
	private final boolean trace;
	private PrintStream out;

	private JavaDiv(ModelSimple model, String foldernameout, String packagename, boolean gwt, boolean trace) {
		this.model = model;
		this.foldernameout = foldernameout;
		this.packagename = packagename;
		this.gwt = gwt;
		this.trace = trace;
	}

	private String getClassnameCopyOf() {
		return gwt ? "ArraysHelper" : "Arrays";
	}
	
	private String getClassnameIsWhitespace() {
		return gwt ? "CharacterHelper" : "Character";
	}
	
	private void run() throws IOException {
		String classname = "_S";
		out = new PrintStream(foldernameout + "/" + classname + ".java", "UTF-8");
		if (packagename!=null && packagename.length()>0) {
			out.print("package ");
			out.print(packagename);
			out.println(";");
		}
		out.println();
		out.println("import java.io.File;");
		if (!gwt) {
			out.println("import java.lang.reflect.Constructor;");
			out.println("import java.lang.reflect.InvocationTargetException;");
		}
		if (gwt) {
			out.println("import treecalc.rt.gwt.CharacterIterator;");
			out.println("import treecalc.rt.gwt.StringCharacterIterator;");
		} else {
			out.println("import java.text.CharacterIterator;");
			out.println("import java.text.StringCharacterIterator;");
		}
		out.println("import java.util.ArrayList;");
		out.println("import java.util.Arrays;");
		out.println("import java.util.HashMap;");
		out.println("import java.util.LinkedHashMap;");
		out.println("import java.util.List;");
		out.println("import java.util.Map;");
		if (trace) {
			out.println("import java.io.PrintWriter;");
			out.println("import java.io.FileWriter;");
		}
		out.println();
		out.println("import treecalc.rt.CacheKey;");
		out.println("import treecalc.rt.ExceptionNeedMoreInput;");
		out.println("import treecalc.rt.LruCache;");
		out.println("import treecalc.rt.V;");
		out.println("import treecalc.rt.VDouble;");
		out.println("import treecalc.rt.VNull;");
		out.println("import treecalc.rt.S;");
		if(gwt) {
			out.println("import treecalc.rt.gwt." + getClassnameCopyOf() + ";");
			out.println("import treecalc.rt.gwt." + getClassnameIsWhitespace() + ";");
		}
		out.println();
		out.println("/**");
		out.println(" * Status including input values, times-counter etc.");
		out.println(" * @author Stefan");
		out.println(" */");
		out.print("public final class ");
		out.print(classname);
		out.print(" implements S ");
		out.println(" {");
		out.println("   /**");
		out.println("    * values for inputs without indizes, or with all '0' indizes.");
		out.println("    * Those are NOT written into inputvalues, just into this \"fast access\" array.");
		out.println("    */");
		out.print  ("   private V[] inputvalues0dim = new V[");
		out.print  (model.getInputSize());
		out.println("];");
		out.println();
		out.println("   /**");
		out.println("    * HashMap<IntList, V> first value is inputid, then indizes follow.");
		out.println("    *        The last index is >0 for sure,");
		out.println("    *        minimum length of the int[] is 2 (otherwise the value lands in inputvalues0dim).");
		out.println("    */");
		out.println("   private HashMap<IntList, V> inputvalues = new HashMap<IntList, V>(1024, 0.5f);");
		out.println();

		out.print("   public final static String[] ina = new String[");
		out.print(model.getInputSize());
		out.println("];");
		out.print("   public final static HashMap<String,Integer> in = new HashMap<String,Integer>(");
		out.print(model.getInputSize());
		out.println(", 0.5f);");
		out.println("   static {");
		out.println("      final String[] i = ina;");
		for (int i=0; i<model.getInputSize(); i++) {
			GenInput input = model.getInput(i);
			String name = input.getName();
			int    ind  = input.getIndex();
			out.print  ("      i[");
			out.print  (ind);
			out.print  ("]=\"");
			out.print  (name);
			out.println("\";");
		}
		out.print  ("      for (int ind=0; ind<");
		out.print  (model.getInputSize());
		out.println("; ind++) {");
		out.println("         in.put(i[ind], ind);");
		out.println("      }");
		out.println("   } //end of static initializer");
		
		out.println();
		out.println("   public final static int getInputIndex(String nameUppercase) {");
		out.println("      Integer i = in.get(nameUppercase);");
		out.println("      return i!=null ? i : -1;");
		out.println("   }");
		out.println("   @Override");
		out.println("   public void reset() {");
		out.print  ("      inputvalues0dim = new V[");
		out.print  (model.getInputSize());
		out.println("];");
		out.print  ("      inputvalues     = new HashMap<IntList, V>(Math.max(inputvalues.size(),");
		out.print  (model.getInputSize());
		out.println("), 0.5f);");
		out.println("   }");
		
		out.print("   /* input calc names (e.g. visible) */");
		out.print("   public final static HashMap<String,Integer> ic = new HashMap<String,Integer>(");
		out.print(model.getCalcnamesSize());
		out.println(", 0.2f);");
		out.println("   static { //initializer for input calc names in ic");
		out.println("      final HashMap<String,Integer> c = ic;");
		for (int i=0; i<model.getInputcalcSize(); i++) {
			String calcname = model.getInputcalcName(i);
			out.print  ("      c.put(");
			out.print  ('"');
			out.print  (calcname);
			out.print  ('"');
			out.print  (", ");
			out.print  (i);
			out.println(");");
		}
		out.println("   } //end of static initializer for ic");
		out.println();
		out.println("   private static int getInputCalcIndex(final String nameUpper) {");
		out.println("      Integer ind = ic.get(nameUpper);");
		out.println("      return ind!=null ? ind : -1;");
		out.println("   }");		
		out.println();
		out.println("   /**");
		out.println("    * Get input as set from outside.");
		out.println("    * Indizes are normalized by cutting of trailing zeros.");
		out.println("    * @param inputid");
		out.println("    * @param index");
		out.println("    * @return null if notfound, the value as V otherwise");
		out.println("    */");
		out.println("   @Override");
		out.println("   public final V getInput(final int inputid, final V...index) throws ExceptionNeedMoreInput {");
		out.println("      final int indexlen = index!=null ? index.length : 0;");
		out.println("      if (indexlen==0) {");
		out.println("         final V ret = inputvalues0dim[inputid];");
		out.println("         if (ret!=null) {");
		out.println("            return ret;");
		out.println("         } else {");
		out.println("            throw new ExceptionNeedMoreInput(ina[inputid], inputid, (int[])null);");
		out.println("         }");
		out.println("      }");
		out.println("      int indNonZero=-1;");
		out.println("      int[] indarr = new int[indexlen];");
		out.println("      for (int i=0; i<indexlen; i++) {");
		out.println("         int ind = (int) index[i].longValue();");
		out.println("         if (ind>0) {");
		out.println("            indNonZero=i;");
		out.println("            indarr[i]=ind;");
		out.println("         }");
		out.println("      }");
		out.println("      if (indNonZero<0) {");
		out.println("         final V ret = inputvalues0dim[inputid];");
		out.println("         if (ret!=null) {");
		out.println("            return ret;");
		out.println("         } else {");
		out.println("            throw new ExceptionNeedMoreInput(ina[inputid], inputid, (int[])null);");
		out.println("         }");
		out.println("      }");
		out.println("      final V ret = inputvalues.get(new IntList(inputid, indarr, indNonZero+1));");
		out.println("      if (ret!=null) {");
		out.println("         return ret;");
		out.println("      } else {");
		out.println("         throw new ExceptionNeedMoreInput(ina[inputid], inputid, indarr);");
		out.println("      }");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public boolean getInputIsNull(String name) {");
		out.println("      final Object[] parsedname = parseSetvarName(name);");
		out.println("      if (parsedname==null) {");
		out.println("         return true;");
		out.println("      }");
		out.println("      final String justnameUpper = (String) parsedname[0];");
		out.println("      final int[]  index = (int[]) parsedname[1];");
		out.println("      final int inputid = getInputIndex(justnameUpper);");
		out.println("      if (inputid<0) {");
		out.println("         return true;");
		out.println("      }");
		out.println("      V[] vindex;");
		out.println("      if (index==null) {");
		out.println("         vindex = new V[0];");
		out.println("      } else {");
		out.println("         int indexlen = index.length;");
		out.println("         vindex = new V[indexlen];");
		out.println("         for (int i=0; i<indexlen; i++) {");
		out.println("            vindex[i] = V.getInstance(index[i]);");
		out.println("         }");
		out.println("      }");
		out.println("      try {");
		out.println("         getInput(inputid, vindex);");
		out.println("         return false;");
		out.println("      } catch (ExceptionNeedMoreInput e) {");
		out.println("         return true;");
		out.println("      }");
		out.println("   }");
		
		out.println();
		out.println("   @Override");
		out.println("   public V[] getAutocounterValues(int[] autocounters) {");
		out.println("      /* get values of active counters */");
		out.println("      int clen = autocounters.length;");
		out.println("      V[] valarr = new V[clen]; /* might be shortened afterwards */");
		out.println("      int valarrsize=0;");
		out.println("      int lastnotzero=-1;");
		out.println("      for (int i=0; i<clen; i++) {");
		out.println("         int c = autocounters[i];");
		out.println("         /* search appropriate active counter */");
		out.println("         for (int j=timesSize; --j>=0;) {");
		out.println("            long cand = timesStack[j];");
		out.println("            int  id = (int) cand;");
		out.println("            if (id==c) {");
		out.println("               int val = (int) (cand >> 32);");
		out.println("               if(val>0) {");
		out.println("                  lastnotzero=valarrsize;");
		out.println("                  valarr[valarrsize] = V.getInstance(val);");
		out.println("               } else {");
		out.println("                  valarr[valarrsize] = VDouble.vdbl0;");
		out.println("               }");
		out.println("               valarrsize++;");
		out.println("               break;");
		out.println("            }");
		out.println("         }");
		out.println("      }");
		out.println("      if (lastnotzero+1<clen) {");
		out.println("         return " + getClassnameCopyOf() + ".copyOf(valarr, lastnotzero+1);");
		out.println("      } else {");
		out.println("         return valarr;");
		out.println("      }");
		out.println("   }");
		out.println();
		out.println();
		out.println("   @Override");
		out.println("   public V getInputAutocounter(int inputid, int[] autocounters) {");
		out.println("      /* get values of active counters */");
		out.println("      int clen = autocounters.length;");
		out.println("      int[] valarr = new int[clen]; /* might be shortened afterwards */");
		out.println("      int valarrsize=0;");
		out.println("      int lastnotzero=-1;");
		out.println("      for (int i=0; i<clen; i++) {");
		out.println("         int c = autocounters[i];");
		out.println("         /* search appropriate active counter */");
		out.println("         for (int j=timesSize; --j>=0;) {");
		out.println("            long cand = timesStack[j];");
		out.println("            int  id = (int) cand;");
		out.println("            if (id==c) {");
		out.println("               int val = (int) (cand >> 32);");
		out.println("               if(val>0) {");
		out.println("                  lastnotzero=valarrsize;");
		out.println("                  valarr[valarrsize] = val;");
		out.println("               }");
		out.println("               valarrsize++;");
		out.println("               break;");
		out.println("            }");
		out.println("         }");
		out.println("      }");
		out.println("      if (lastnotzero<0) {");
		out.println("         final V ret = inputvalues0dim[inputid];");
		out.println("         if (ret!=null) {");
		out.println("            return ret;");
		out.println("         } else {");
		out.println("            throw new ExceptionNeedMoreInput(ina[inputid], inputid, (int[])null);");
		out.println("         }");
		out.println("      } else {");
		out.println("         final V ret = inputvalues.get(new IntList(inputid, valarr, lastnotzero+1));");
		out.println("         if (ret!=null) {");
		out.println("            return ret;");
		out.println("         } else {");
		out.println("            throw new ExceptionNeedMoreInput(ina[inputid], inputid, valarr);");
		out.println("         }");
		out.println("      }");
		out.println("   }");


		out.println();
		out.println("   @Override");
		out.println("   public final void setInput(int inputid, V value, int...index) {");
		out.println("      if (!cacheEmpty) {");
		out.println("         initCache();");
		out.println("      }");
		out.println("      int indexlen = index.length;");
		out.println("      if (indexlen==0) {");
		out.println("         inputvalues0dim[inputid]=value;");
		out.println("      }");
		out.println("      int i;");
		out.println("      for (i=index.length; --i>=0; ) {");
		out.println("         int ind = index[i];");
		out.println("         if (ind>0) {");
		out.println("            break;");
		out.println("         }");
		out.println("      }");
		out.println("      if (i<0) {");
		out.println("         inputvalues0dim[inputid]=value;");
		out.println("         return;");
		out.println("      }");
		out.println("      inputvalues.put(new IntList(inputid, index, i+1), value);");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public final void setInput(String name, String value) {");
		out.println("      if (!cacheEmpty) {");
		out.println("         initCache();");
		out.println("      }");
		out.println("      Object[] parsedname = parseSetvarName(name);");
		out.println("      if (parsedname==null) {");
		out.println("         return;");
		out.println("      }");
		out.println("      String justnameUpper = (String) parsedname[0];");
		out.println("      int[]  index = (int[]) parsedname[1];");
		out.println("      int inputid = getInputIndex(justnameUpper);");
		out.println("      if (index==null) {");
		out.println("         inputvalues0dim[inputid]=V.getInstance(value);");
		out.println("         return;");
		out.println("      }");
		out.println("      inputvalues.put(new IntList(inputid, index, index.length), V.getInstance(value));");
		out.println("   }");
		out.println();
		out.println("   @Override @SuppressWarnings(\"unchecked\")");
		out.println("   public List<String[]> getInputList(int inputid, int...index) {");
		out.println("      if (index==null) {");
		out.println("      	  return (List<String[]>) I.dynamicCall(inputid, -1, this);");
		out.println("      }");
		out.println("      int indexlen = index.length;");
		out.println("      V[] vindex = new V[indexlen];");
		out.println("      for (int i=0; i<indexlen; i++) {");
		out.println("         vindex[i] = V.getInstance(index[i]);");
		out.println("      }");
		out.println("      return (List<String[]>) I.dynamicCall(inputid, -1, this, vindex);");
		out.println("   }");
		out.println("   /*");
		out.println("    * name: inputname, case insensitive, optional with indizes in [] separted by ,");
		out.println("    * e.g. A_Age, A_Age[5,3], A_Age[0, 00, 1]");
		out.println("    */");
		out.println("   @Override");
		out.println("   public List<String[]> getInputList(String name) {");
		out.println("         Object[] parsedname = parseSetvarName(name);");
		out.println("         if (parsedname==null) {");
		out.println("            return null;");
		out.println("         }");
		out.println("         String justnameUpper = (String) parsedname[0];");
		out.println("         int[]  index = (int[]) parsedname[1];");
		out.println("         int inputid = getInputIndex(justnameUpper);");
		out.println("         return getInputList(inputid, index);");
		out.println("   }");

		out.println();
		out.println("   public String calculateMaintree(int calcindex, String...args) {");
		List<ScopeNode> mainchildren = model.maintreeScope.getChildren();
		if (mainchildren!=null && mainchildren.size()>0) {
			ScopeNode mainnode = mainchildren.get(0);
			NodeData mainnodeData = model.getNodeData(mainnode);
			int mainnodeindex = mainnodeData.nodeindex; 
			out.println("      int arglen = args!=null ? args.length : 0;");
			out.println("      if(arglen>0) {");
			out.println("         V[] argsV = new V[arglen];");
			out.println("         for (int i=0; i<arglen; i++) {");
			out.println("            argsV[i] = V.getInstance(args[i]);");
			out.println("         }");
			out.print  ("         return Nodes.cmt(");
			out.println("calcindex, this, argsV).stringValue();");
			out.println("      } else {");
			out.print  ("         return Nodes.cmt(");
			out.println("calcindex, this).stringValue();");
			out.println("      }");
		} else {
			out.println("      throw new RuntimeException(\"the main tree is empty, can not compute anything there\");"); 
		}
		out.println("   }");

		out.println();
		out.println("   @Override");
		out.println("   public String calculateMaintree(String name) {");
		out.println("      Object[] parseRet = parseCalcname(name);");
		out.println("      String calcname = parseRet!=null ? (String) parseRet[0] : null;");
		out.println("      int calcindex = Nodes.getCalcIndex(calcname);");
		out.println("      if(calcindex>=0) {");
		out.println("         return Nodes.cmt(calcindex, this, (V[]) parseRet[1]).stringValue();");
		out.println("      } else {");
		out.println("        throw new RuntimeException(\"invalid calculation name\" + name);"); 
		out.println("      }");
		out.println("   }");
		
		out.println();
		out.println("    /**");
		out.println("     * @param setvarname: name[index1;index2;index3;...;indexN]");
		out.println("     * whitespaces enthalten -> diese sind dann TEIL DES NAMENS index :");
		out.println("     * whitespaces vor index ok; nach index fehler; fuehrende 0 ok index");
		out.println("     * weggelassen -> wie 0");
		out.println("      * name[index1;...;indexN;0;...;0] -> wie name[index1;...;indexN]");
		out.println("     * @return null on error or Object array of length 2");
		out.println("         *    ret[0]: name without index, everything uppercase");
		out.println("         *    ret[1]: int[] with normalized indizes, or null is there is none");
		out.println("     */");
		out.println("   private final static Object[] parseSetvarName(String setvarname) {");
		out.println("      /*  Examples");
		out.println("       *   parseSetvarName(\"A_Test[3;5]\") = \"A_Test;3;5\"");
		out.println("       *   parseSetvarName(\"A_Test[ 03; 05; 00]\") = \"A_Test;3;5\"");
		out.println("       *   parseSetvarName(\"A_Test[0;0]\") = \"A_Test\"");
		out.println("       *   parseSetvarName(\"A_Test[0;0;1]\") = \"A_Test;;1\"");
		out.println("       *   parseSetvarName(\"A_Test[;;1]\") = \"A_Test;;1\"");
		out.println("       *");
		out.println("       *   //A_Test[1]=ok");
		out.println("       *   //A_Test[;1]=ok");
		out.println("       *   //A_Test[1;;2]=ok");
		out.println("       *   //A_Test=error");
		out.println("       *   //A_Test =error");
		out.println("       *   //A_Test [1]=error");
		out.println("       *   //A_Test[1] =ok");
		out.println("       *   //A_Test[ 1; 2]=ok");
		out.println("       *   //A_Test[ 1 ; 2]=error");
		out.println("       *   //A_Test[ 1;;]=ok");
		out.println("       *   //A_Test[  ;;2]=ok");
		out.println("       *   //A_Test[  ;  ; 2]=ok");
		out.println("       *   //A_Test[  ;  ; ]=ok");
		out.println("       *   //A_Test[+1]=error");
		out.println("       *   //A_Test[001]=ok");
		out.println("       *");
		out.println("       */");
		out.println("      if (setvarname == null) {");
		out.println("         return new Object[] { \"\", null };");
		out.println("      }");
		out.println("      int slen = setvarname.length();");
		out.println("      if (slen == 0) {");
		out.println("         return new String[] { \"\", null };");
		out.println("      }");
		out.println("      int indStartBracket = setvarname.indexOf('[');");
		out.println("      /* no opening bracket -> everything is the name */");
		out.println("      if (indStartBracket<0) {");
		out.println("         String ret = setvarname.toUpperCase();");
		out.println("         return new Object[] { ret, null };");
		out.println("      }");
		out.println("      /* name: everything up to '[' */");
		out.println("      String name = setvarname.substring(0, indStartBracket).toUpperCase();");
		out.println();
		out.println("      int[] ind = new int[10];");
		out.println("      // start parsing after initial '['");
		out.println("      StringCharacterIterator i = new StringCharacterIterator(setvarname, indStartBracket+1);");
		out.println("      char c = i.current();");
		out.println("      int lastNotZero = -1;");
		out.println("      // get arguments");
		out.println("      for (int indind=0; c != StringCharacterIterator.DONE; indind++) {");
		out.println("         while (c==' ' || c=='\\t' || c=='\\f' || c=='\\r' || c=='\\n') { //eat whitespaces");
		out.println("            c = i.next();");
		out.println("         }");
		out.println("         int index = 0;");
		out.println("         loop1: while(true) {");
		out.println("            switch(c) {");
		out.println("            case '0': index*=10; break;");
		out.println("            case '1': index*=10; index+=1; break;");
		out.println("            case '2': index*=10; index+=2; break;");
		out.println("            case '3': index*=10; index+=3; break;");
		out.println("            case '4': index*=10; index+=4; break;");
		out.println("            case '5': index*=10; index+=5; break;");
		out.println("            case '6': index*=10; index+=6; break;");
		out.println("            case '7': index*=10; index+=7; break;");
		out.println("            case '8': index*=10; index+=8; break;");
		out.println("            case '9': index*=10; index+=9; break;");
		out.println("            default: break loop1;");
		out.println("            }");
		out.println("            c = i.next();");
		out.println("         }");
		out.println("         if (index>0) {");
		out.println("            lastNotZero = indind;");
		out.println("            if (indind>ind.length) {");
		out.println("               ind = " + getClassnameCopyOf() + ".copyOf(ind, indind*2);");
		out.println("            }");
		out.println("            ind[indind] = index;");
		out.println("         }");
		out.println("         if (c != ';' && c!=',') {");
		out.println("            break;");
		out.println("         }");
		out.println("         c = i.next();");
		out.println("      }");
		out.println("      if (c != ']') {");
		out.println("         return null;");
		out.println("      }");
		out.println("      c = i.next();");
		out.println("      while (c==' ' || c=='\\t' || c=='\\f' || c=='\\r' || c=='\\n') {");
		out.println("         c = i.next();");
		out.println("      }");
		out.println("      if (c != StringCharacterIterator.DONE) {");
		out.println("         return null;");
		out.println("      }");
		out.println("      if (lastNotZero<0) {");
		out.println("         return new Object[] { name, null };");
		out.println("      }");
		out.println("      int[] ret = " + getClassnameCopyOf() + ".copyOf(ind, lastNotZero+1);");
		out.println("      return new Object[] { name, ret };");
		out.println("   }");
		out.println();
		out.println("   private static class IntList {");
		out.println("      long key;");
		out.println("      int[] vals;");
		out.println("      int keylen;");
		out.println("      IntList(int ikey, int[] vals, int len) {");
		out.println("         if (len<=0) {");
		out.println("            this.key = ikey;");
		out.println("            return;");
		out.println("         }");
		out.println("         this.key = (long) ikey << 32 + vals[0];");
		out.println("         if (len>1) {");
		out.println("            keylen = len-1;");
		out.println("            int[] v = new int[keylen];");
		out.println("            System.arraycopy(vals, 1, v, 0, keylen);");
		out.println("            this.vals = v;");
		out.println("         }");
		out.println("      }");
		out.println();
		out.println("      @Override");
		out.println("      public int hashCode() {");
		out.println("         final int prime = 31;");
		out.println("         int result = 1;");
		out.println("         result = prime * result + (int) (key ^ (key >>> 32));");
		out.println("         result = prime * result + keylen;");
		out.println("         result = prime * result + Arrays.hashCode(vals);");
		out.println("         return result;");
		out.println("      }");
		out.println();
		out.println("      @Override");
		out.println("      public boolean equals(Object obj) {");
		out.println("         IntList other = (IntList) obj;");
		out.println("         if (key != other.key)");
		out.println("            return false;");
		out.println("         if (keylen != other.keylen)");
		out.println("            return false;");
		out.println("         if (!Arrays.equals(vals, other.vals))");
		out.println("            return false;");
		out.println("         return true;");
		out.println("      }");
		out.println("   }");

		out.println();
		out.println("   private final static char getInteger(CharacterIterator i, char c, StringBuffer token) {");
		out.println("      if (!Character.isDigit(c)) {");
		out.println("         throw new RuntimeException(\"invalid argument: Integer expected\");");
		out.println("      }");
		out.println("      while (Character.isDigit(c)) {");
		out.println("         token.append(c);");
		out.println("         c=i.next();");
		out.println("      }");
		out.println("      return c;");
		out.println("   }");
		out.println();
		out.println("   private static char getExponent(CharacterIterator i, char c, StringBuffer token) {");
		out.println("      // EXPONENT => e[-]?[0-9]+");
		out.println("      if (c!='e' && c!='E')");
		out.println("         throw new RuntimeException(\"invalid argument: exponent expected\");");
		out.println("      token.append('e');");
		out.println("      c=i.next();");
		out.println("      if (c=='-') {");
		out.println("         token.append('-');");
		out.println("         c=i.next();");
		out.println("      }");
		out.println("      return getInteger(i, c, token);");
		out.println("   }");
		
		out.println();
		out.println("/**");
		out.println(" * @param calcname: F_Test, F_Test(1, 2), A_Test[5], A_Test[5].visible, T_Table[3, 5], T_Table[3].text ");
		out.println(" * @return  Object[5]:");
		out.println(" *          [0]: String nameUppercase + ':' + args.length");
		out.println(" *          [1]: V[] args");
		out.println(" *          [2]: nameUppercase");
		out.println(" *          [3]: String calcnameUppercase (or null)");
		out.println(" *          [4]: Integer type - 0:no parens, 1:(), 2:[]");
		out.println(" */");
		out.println("   public final static Object[] parseCalcname(String calcname) {");
		out.println("      if (calcname == null || calcname.length() == 0) {");
		out.println("         return null;");
		out.println("      }");
		out.println("      char c;");
		out.println("      List<V> arg=null;");
		out.println("      StringBuffer token;");
		out.println("      StringCharacterIterator i = new StringCharacterIterator(calcname);");
		out.println("      int type = 0; // 0: no parenthesis; 1:function; 2:table");
		out.println("      token = new StringBuffer(30);");
		out.println("      /* skip leading whitespaces */");
		out.println("      c = i.first();");
		out.println("      while (" + getClassnameIsWhitespace() + ".isWhitespace(c)) {");
		out.println("         c = i.next();");
		out.println("      }");
		out.println("      int inddot=-1;");
		out.println("      while (!(c == '(' || c == '[' || " + getClassnameIsWhitespace() + ".isWhitespace(c) || c == StringCharacterIterator.DONE)) {");
		out.println("         token.append(Character.toUpperCase(c));");
		out.println("         if (c=='.') {");
		out.println("            inddot=i.getIndex();");
		out.println("         }");
		out.println("         c = i.next();");
		out.println("      }");
		out.println("      String name = token.toString();");
		out.println("");
		out.println("      /* eat whitespaces before '(' */");
		out.println("      while (" + getClassnameIsWhitespace() + ".isWhitespace(c)) {");
		out.println("         c = i.next();");
		out.println("      }");
		out.println("");
		out.println("      /* eat (|[ */");
		out.println("      if (c == '(') {");
		out.println("         type = 1; // function with parameters");
		out.println("         c = i.next();");
		out.println("      } else if (c == '[') { // table access");
		out.println("         type = 2;");
		out.println("         c = i.next();");
		out.println("      } else { ");
		out.println("         type = 0; // no arguments");
		out.println("      }");
		out.println("      while (" + getClassnameIsWhitespace() + ".isWhitespace(c)) {");
		out.println("         c = i.next();");
		out.println("      }");
		out.println("      if (type==0 && c!=StringCharacterIterator.DONE) {");
		out.println("         return null;");
		out.println("      }");
		out.println("      boolean first=true;");
		out.println("      /* get arguments */");
		out.println("      while (c != StringCharacterIterator.DONE) {");
		out.println("         token = new StringBuffer(5);");
		out.println("         /* are at beginning of next argument */");
		out.println("         while (" + getClassnameIsWhitespace() + ".isWhitespace(c)) {");
		out.println("            c = i.next();");
		out.println("         }");
		out.println("         if ((type==1 && c==')') || (type==2 && c==']')) {");
		out.println("            break;");
		out.println("         }");
		out.println("         if (!first && (c==',' || c==';')) {");
		out.println("            c=i.next();");
		out.println("            while (" + getClassnameIsWhitespace() + ".isWhitespace(c)) {");
		out.println("               c = i.next();");
		out.println("            }");
		out.println("         }");
		out.println("");
		out.println("         first=false;");
		out.println("         if (c == '\"') {");
		out.println("            c = i.next();");
		out.println("            while (true) {");
		out.println("               if (c == '\"') {");
		out.println("                  c = i.next();");
		out.println("                  if (c == '\"') {");
		out.println("                     token.append(c);");
		out.println("                     c = i.next();");
		out.println("                  } else {");
		out.println("                     break;");
		out.println("                  }");
		out.println("               } else {");
		out.println("                  if (c==StringCharacterIterator.DONE) {");
		out.println("                     return null;");
		out.println("                  }");
		out.println("                  token.append(c);");
		out.println("                  c = i.next();");
		out.println("               }");
		out.println("            }");
		out.println("            if (arg==null) {");
		out.println("               arg = new ArrayList<V>(3);");
		out.println("            }");
		out.println("            arg.add(V.getInstance(token.toString()));");
		out.println("            token = new StringBuffer(5); // 5 as this is called after the name only -> indizes will follow, those are short");
		out.println("         } else {");
		out.println("            //  -?{INTEGER}");
		out.println("            //  -?{INTEGER}[.,]?{EXPONENT}");
		out.println("            //  -?{INTEGER}[.,]{INTEGER}{EXPONENT}?");
		out.println("            //  -?[.,]{INTEGER}{EXPONENT}?     ohne - mit .: gefaehrlicher Fall");
		out.println("            if (c=='-') {");
		out.println("               //gotMinus=true;");
		out.println("               token.append(c);");
		out.println("               c=i.next();");
		out.println("            }");
		out.println("            if (c==',' || c=='.') {");
		out.println("               //  [.,]{INTEGER}{EXPONENT}?");
		out.println("               token.append('.');");
		out.println("               c=i.next();");
		out.println("               c=getInteger(i, c, token);");
		out.println("               if(c=='e' || c=='E')");
		out.println("                  c=getExponent(i, c, token);");
		out.println("");
		out.println("               if (arg==null) {");
		out.println("                  arg = new ArrayList<V>(3);");
		out.println("               }");
		out.println("               arg.add(V.getInstance(token.toString()));");
		out.println("               token = new StringBuffer(5); // 5 as this is called after the name only -> indizes will follow, those are short");
		out.println("            }");
		out.println("            else {");
		out.println("               //  {INTEGER}");
		out.println("               //  {INTEGER}[.,]?{EXPONENT}");
		out.println("               //  {INTEGER}[.,]{INTEGER}{EXPONENT}?");
		out.println("               boolean done=false;");
		out.println("               if (!Character.isDigit(c))");
		out.println("                  return null;");
		out.println("               c = getInteger(i, c, token);");
		out.println("               if (c=='.' || c==',') {");
		out.println("                  // special Case: {INTEGER} gefolgt von ',' als Parameter-Trennzeichen");
		out.println("                  //  {INTEGER}[.,]{EXPONENT}");
		out.println("                  //  {INTEGER}[.,]{INTEGER}{EXPONENT}?");
		out.println("                  if (c==',') {");
		out.println("                     c=i.next();");
		out.println("                     if (!Character.isDigit(c) && c!='e' && c!='E') {");
		out.println("                        done=true;");
		out.println("                        if (arg==null) {");
		out.println("                           arg = new ArrayList<V>(3);");
		out.println("                        }");
		out.println("                        arg.add(V.getInstance(token.toString()));");
		out.println("                        token = new StringBuffer(5); // 5 as this is called after the name only -> indizes will follow, those are short");
		out.println("                     }");
		out.println("                     c=i.previous();");
		out.println("                  }");
		out.println("                  if (!done) {");
		out.println("                     token.append('.');");
		out.println("                     c=i.next();");
		out.println("                     if (c=='e' || c=='E') {");
		out.println("                        c = getExponent(i, c, token);");
		out.println("                        if (arg==null) {");
		out.println("                           arg = new ArrayList<V>(3);");
		out.println("                        }");
		out.println("                        arg.add(V.getInstance(token.toString()));");
		out.println("                        token = new StringBuffer(5); // 5 as this is called after the name only -> indizes will follow, those are short");
		out.println("                     } else if (Character.isDigit(c)) {");
		out.println("                        c = getInteger(i, c, token);");
		out.println("                        if (c=='e' || c=='E')");
		out.println("                           c = getExponent(i, c, token);");
		out.println("                        if (arg==null) {");
		out.println("                           arg = new ArrayList<V>(3);");
		out.println("                        }");
		out.println("                        arg.add(V.getInstance(token.toString()));");
		out.println("                        token = new StringBuffer(5); // 5 as this is called after the name only -> indizes will follow, those are short");
		out.println("                     } else");
		out.println("                        return null;");
		out.println("");
		out.println("                  }");
		out.println("               } else if (c=='e' || c=='E'){");
		out.println("                  //  {INTEGER}{EXPONENT}");
		out.println("                  c = getExponent(i, c, token);");
		out.println("                  if (arg==null) {");
		out.println("                     arg = new ArrayList<V>(3);");
		out.println("                  }");
		out.println("                  arg.add(V.getInstance(token.toString()));");
		out.println("                  token = new StringBuffer(5); // 5 as this is called after the name only -> indizes will follow, those are short");
		out.println("               } else { ");
		out.println("                  //  {INTEGER}");
		out.println("                  if (arg==null) {");
		out.println("                     arg = new ArrayList<V>(3);");
		out.println("                  }");
		out.println("                  arg.add(V.getInstance(token.toString()));");
		out.println("                  token = new StringBuffer(5); // 5 as this is called after the name only -> indizes will follow, those are short");
		out.println("               }");
		out.println("            }");
		out.println("         } // end of parsing one argument");
		out.println("");
		out.println("         while (" + getClassnameIsWhitespace() + ".isWhitespace(c)) {");
		out.println("            c = i.next();");
		out.println("         }");
		out.println("      }");
		out.println("");
		out.println("      if (type==1 || type==2) {");
		out.println("         if (type==1 && c!=')') {");
		out.println("            throw new RuntimeException (\"')' missing\");");
		out.println("         }");
		out.println("         if (type==2 && c!=']') {");
		out.println("            throw new RuntimeException (\"']' missing\");");
		out.println("         }");
		out.println("         c=i.next();");
		out.println("      }");
		out.println("");
		out.println("      while (" + getClassnameIsWhitespace() + ".isWhitespace(c)) {");
		out.println("         c = i.next();");
		out.println("      }");
		out.println("      ");
		out.println("      String subcalcname = null;");
		out.println("      if (c=='.') {");
		out.println("         subcalcname = calcname.substring(i.getIndex()+1).toUpperCase();");
		out.println("         c = StringCharacterIterator.DONE;");
		out.println("      }");
		out.println("");
		out.println("      if (c!=StringCharacterIterator.DONE) {");
		out.println("         return null;");
		out.println("      }");
		out.println("");
		out.println("      V[] args;");
		out.println("      if (arg==null) {");
		out.println("         args = new V[0];");
		out.println("      } else {");
		out.println("         args = arg.toArray(new V[0]);");
		out.println("      }");
		out.println("      if (inddot>=0 && type==0) {");
		out.println("         subcalcname = name.substring(inddot+1);");
		out.println("         name = name.substring(0, inddot);");
		out.println("      }");
		out.println("      Object[] ret = new Object[] { name+':'+args.length, args, name, subcalcname, type };");
		out.println("      return ret;");
		out.println("   }");

		
		out.println("   /**");
		out.println("    * left  32bit: counter");
		out.println("    * right 32bit: timesid ");
		out.println("    */");
		for (int i=0; i<model.getTimesSize(); i++) {
			String countername = model.getTimesName(i);
			out.println("   //" + i + ": " + countername);
		}
		out.println("   private long[] timesStack = new long[10];");
		out.println("   private int    timesLen   = 10;");
		out.println("   private int    timesSize  = 0;");
		out.println("   @Override");
		out.println("   public void pushTimesCounter(int timesid) {");
		out.println("      long newval = timesid;");
		out.println("      if (timesLen<=timesSize) {");
		out.println("         timesLen <<= 1;");
		out.println("         timesStack = " + getClassnameCopyOf() + ".copyOf(timesStack, timesLen);");
		out.println("      }");
		out.println("      timesStack[timesSize++] = newval;");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public void incTimesCounterTop() {");
		out.println("      timesStack[timesSize-1] += 0x100000000L;");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public void setTimesCounterTop(int value) {");
		out.println("      int i = timesSize-1;");
		out.println("      int counterid = (int) timesStack[i];");
		out.println("      long newval = ((long)value<<32) + counterid;");
		out.println("      timesStack[i] = newval;");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public void popTimesCounter() {");
		out.println("      timesSize--;");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public int getTimesCounter(int timesid) {");
		out.println("      for (int i=timesSize; --i>=0;) {");
		out.println("         long cand = timesStack[i];");
		out.println("         int  id = (int) cand;");
		out.println("         if (id==timesid) {");
		out.println("            return (int) (cand >> 32);");
		out.println("         }");
		out.println("      }");
		out.println("      return 0;");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public V getTimesCounterV(int timesid) {");
		out.println("      for (int i=timesSize; --i>=0;) {");
		out.println("         long cand = timesStack[i];");
		out.println("         int  id = (int) cand;");
		out.println("         if (id==timesid) {");
		out.println("            int val = (int) (cand >> 32);");
		out.println("            return V.getInstance(val);");
		out.println("         }");
		out.println("      }");
		out.println("      return VDouble.vdbl0;");
		out.println("   }");
		out.println();
		out.println("   private final int maxCachesize;");
		out.println("   private final float loadFactor;");
		out.println("   public _S() { this(10000, 0.5f); };");
		out.println("   public _S(int maxCachesize) { this(maxCachesize, 0.5f); }");
		out.println("   public _S(int maxCachesize, float loadFactor) {");
		out.println("      this.maxCachesize = maxCachesize;");
		out.println("      this.loadFactor = loadFactor;");
		out.println("      initCache();");
		out.println("   }");
		out.println("   private void initCache() {");
		out.println("      cache = new LruCache<Object,V>(this.maxCachesize, this.loadFactor);");
		out.println("      cacheEmpty = true;");
		out.println("   }");

		out.println();
		out.println("   private boolean cacheEmpty;");
		out.println("   private LruCache<Object,V> cache;");

		out.println();
		out.println("   @Override");
		out.println("   public Object getCacheKey(final int id, final V...args) {");
		out.println("      return new CacheKey(id, args, timesSize==0 ? null : " + getClassnameCopyOf() + ".copyOf(timesStack, timesSize));");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public V readCache(final Object key) {");
		out.println("      return cache.get(key);");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public void writeCache(final Object key, final V value) {");
		out.println("      cache.put(key, value);");
		out.println("      cacheEmpty = false;");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public String calculateInput(final String name) {");
		out.println("      final Object[] parsedname = parseSetvarName(name);");
		out.println("      if (parsedname==null) {");
		out.println("         return null;");
		out.println("      }");
		out.println("      final String justnameUpper = (String) parsedname[0];");
		out.println("      final int[]  index = (int[]) parsedname[1];");
		out.println("      final int inputid = getInputIndex(justnameUpper);");
		out.println("      if (inputid<0) {");
		out.println("         throw new RuntimeException(\"Invalid inputname \" + name);");
		out.println("      }");
		out.println("      V[] vindex;");
		out.println("      if (index==null) {");
		out.println("         vindex = new V[0];");
		out.println("      } else {");
		out.println("         int indexlen = index.length;");
		out.println("         vindex = new V[indexlen];");
		out.println("         for (int i=0; i<indexlen; i++) {");
		out.println("            vindex[i] = V.getInstance(index[i]);");
		out.println("         }");
		out.println("      }");
		out.println("      final V ret = (V) I.dynamicCall(inputid, -2, this, vindex);");
		out.println("      return ret.stringValue();");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public String calculateInputCalc(String name, String calcname) {");
		out.println("      final Object[] parsedname = parseSetvarName(name);");
		out.println("      if (parsedname==null) {");
		out.println("         return null;");
		out.println("      }");
		out.println("      final String justnameUpper = (String) parsedname[0];");
		out.println("      final int[]  index = (int[]) parsedname[1];");
		out.println("      final int inputid = getInputIndex(justnameUpper);");
		out.println("      V[] vindex;");
		out.println("      if (index==null) {");
		out.println("         vindex = new V[0];");
		out.println("      } else {");
		out.println("         int indexlen = index.length;");
		out.println("         vindex = new V[indexlen];");
		out.println("         for (int i=0; i<indexlen; i++) {");
		out.println("            vindex[i] = V.getInstance(index[i]);");
		out.println("         }");
		out.println("      }");
		out.println("      final int inputcalcid = getInputCalcIndex(calcname.toUpperCase());");
		out.println("      if (inputcalcid<0) {");
		out.println("         throw new RuntimeException(\"invalid calcname \" + calcname);");
		out.println("      }");
		out.println("      final V ret = (V) I.dynamicCall(inputid, inputcalcid, this, vindex);");
		out.println("      return ret.stringValue();");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public String calculateFunction(String name) {");
		out.println("      Object[] parseRet = parseCalcname(name);");
		out.println("      String funcnameUpper = (String) parseRet[2];");
		out.println("      V[] vargs = (V[]) parseRet[1];");
		out.println("      V funcref = F.getFuncref(funcnameUpper);");
		out.println("      int funcindex = funcref.funcrefValue();");
		out.println("      return F.dynamicCall(funcref, this, vargs).stringValue();");
		out.println("   }");
		out.println();
		out.println("   @Override");
		out.println("   public String calculateTable(String name) {");
		out.println("       Object[] parseRet = parseCalcname(name);");
		out.println("       String tabnameUpper = (String) parseRet[2];");
		out.println("       V[] vargs = (V[]) parseRet[1];");
		out.println("      //int tabindex  = Tables.getTableid(tabnameUpper);");
		out.println("      String colname = (String) parseRet[3];");
		out.println("      final V ret;");
		out.println("      if (colname==null) {");
		out.println("         ret = (V) Tables.dynamicCall(");
		out.println("               V.getInstance(tabnameUpper), ");
		out.println("               Tables.CALL_EXACT, ");
		out.println("               (Object[]) vargs");
		out.println("              );");
		out.println("      } else {");
		out.println("         int lenvargs = vargs.length;");
		out.println("         V[] dynargs = new V[lenvargs+1];");
		out.println("         dynargs[0] = V.getInstance(colname);");
		out.println("         System.arraycopy(vargs, 0, dynargs, 1, lenvargs);");
		out.println("         ret = (V) Tables.dynamicCall(");
		out.println("               V.getInstance(tabnameUpper), ");
		out.println("               Tables.CALL_EXACT_COL,");
		out.println("               (Object[]) dynargs");
		out.println("              );");
		out.println("      }");
		out.println("      return ret.stringValue();");
		out.println("   }");
		out.println("   @Override");
		out.println("   public long getCalculateId(String name) {");
		out.println("      /* <0 if notfound, id to be used with calculate(int id,...) otherwise");
		out.println("       * high int: index; low int: lowest bits encode what it is, higher bits have additional index if necessary");
		out.println("       * calcindex     000.....000: calc  (number of arguments is fixed to the number of arguments we got in name!)");
		out.println("       * inputindex    000.....001: input ");
		out.println("       * inpid         calcid..010: input-calc");
		out.println("       * funcid        000.....011: func");
		out.println("       * tabid         000.....100: table");
		out.println("       * tabid         colind..101: table-col");
		out.println("       */");
		out.println("       Object[] parseRet = parseCalcname(name);");
		out.println("       String nameWithParamcounter = (String) parseRet[0];");
		out.println("       V[] args = (V[]) parseRet[1];");
		out.println("       String nameUpper = (String) parseRet[2];");
		out.println("       String calcnameUppercase = (String) parseRet[3];");
		out.println("       int type = (Integer) parseRet[4];");
		out.println("       switch (type) {");
		out.println("       case 0: { //calc without parameters or input or function without parameters");
		out.println("          if (calcnameUppercase==null) {");
		out.println("             /* calc? */");
		out.println("             final int calcindex = Nodes.getCalcIndex(nameWithParamcounter);");
		out.println("             if (calcindex>=0) {");
		out.println("                return ((long)calcindex<<32) + 0L;");
		out.println("             }");
		out.println("             /* input? */");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex>=0) {");
		out.println("                return ((long)inputindex<<32) + 1L;");
		out.println("             }");
		out.println("             /* function? */");
		out.println("             final int funcindex = F.getFuncIndex(nameUpper);");
		out.println("             if (funcindex>=0) {");
		out.println("                return ((long)funcindex<<32) + 3L;");
		out.println("             }");
		out.println("             /* table? */");
		out.println("             final int tabindex = Tables.getTableid(nameUpper);");
		out.println("             if (tabindex>=0) {");
		out.println("                return ((long)tabindex<<32) + 4L;");
		out.println("             }");
		out.println("             /* calc with number of parameters (:numparam)? */");
		out.println("             final int calcindex2 = Nodes.getCalcIndex(nameUpper);");
		out.println("             if (calcindex2>=0) {");
		out.println("                return ((long)calcindex2<<32) + 0L;");
		out.println("             }");
		out.println("          } else {");
		out.println("             /* input-calc */");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex>=0) {");
		out.println("                final int inputcalcid = getInputCalcIndex(calcnameUppercase);");
		out.println("                if (inputcalcid>=0) {");
		out.println("                   return ((long)inputindex<<32)");
		out.println("                         + (long) ((int)(inputcalcid<<3)) //make sure it doesn't shift into inputindex ");
		out.println("                         + 2L;");
		out.println("                }");
		out.println("             }");
		out.println("             final int tabindex = Tables.getTableid(nameUpper);");
		out.println("             if (tabindex>=0) {");
		out.println("                int colind = (Integer) Tables.dynamicCall(");
		out.println("                      V.getInstanceTabref(tabindex), ");
		out.println("                      Tables.CALL_COLINDEX,");
		out.println("                      calcnameUppercase");
		out.println("                      );");
		out.println("                return ((long)tabindex<<32)");
		out.println("                      + (long) ((int)(colind<<3)) //make sure it doesn't shift into inputindex ");
		out.println("                      + 5L;");
		out.println("             }");
		out.println("          }");
		out.println("          break;");
		out.println("       }");
		out.println("       case 1: { //calc or function");
		out.println("          /* calc? */");
		out.println("          if (calcnameUppercase!=null) {");
		out.println("             break;");
		out.println("          }");
		out.println("          final int calcindex = Nodes.getCalcIndex(nameWithParamcounter);");
		out.println("          if (calcindex>=0) {");
		out.println("            return ((long)calcindex<<32) + 0L;");
		out.println("          }");
		out.println("          /* function? */");
		out.println("          final int funcindex = F.getFuncIndex(nameUpper);");
		out.println("          if (funcindex>=0) {");
		out.println("            return ((long)funcindex<<32) + 3L;");
		out.println("          }");
		out.println("          break;");
		out.println("       }");
		out.println("       case 2: { //input or table");
		out.println("          if (calcnameUppercase==null) {");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex>=0) {");
		out.println("                return ((long)inputindex<<32) + 1L;");
		out.println("             }");
		out.println("             final int tabindex = Tables.getTableid(nameUpper);");
		out.println("             if (tabindex>=0) {");
		out.println("                return ((long)tabindex<<32) + 4L;");
		out.println("             }");
		out.println("             break;");
		out.println("          } else {");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex>=0) {");
		out.println("                final int inputcalcid = getInputCalcIndex(calcnameUppercase);");
		out.println("                if (inputcalcid>=0) {");
		out.println("                   return inputindex + inputcalcid;");
		out.println("                }");
		out.println("                break;");
		out.println("             }");
		out.println("             final int tabindex = Tables.getTableid(nameUpper);");
		out.println("             if (tabindex>=0) {");
		out.println("                int colind = (Integer) Tables.dynamicCall(");
		out.println("                      V.getInstanceTabref(tabindex), ");
		out.println("                      Tables.CALL_COLINDEX,");
		out.println("                      calcnameUppercase");
		out.println("                      );");
		out.println("                return ((long)tabindex<<32)");
		out.println("                      + (long) ((int)(colind<<3)) //make sure it doesn't shift into inputindex ");
		out.println("                      + 5L;");
		out.println("             }");
		out.println("          }");
		out.println("       } //end of case 2");
		out.println("       } //end of switch");
		out.println("       throw new RuntimeException(\"invalid calculation string '\" + name + \"'\");");
		out.println("   }");
		out.println("   @Override");
		out.println("   public String calculate(String name) {");
		out.println("       Object[] parseRet = parseCalcname(name);");
		out.println("       String nameWithParamcounter = (String) parseRet[0];");
		out.println("       V[] args = (V[]) parseRet[1];");
		out.println("       String nameUpper = (String) parseRet[2];");
		out.println("       String calcnameUppercase = (String) parseRet[3];");
		out.println("       int type = (Integer) parseRet[4];");
		out.println("       switch (type) {");
		out.println("       case 0: { //calc without parameters or input or function without parameters");
		out.println("          if (calcnameUppercase==null) {");
		out.println("             /* calc? */");
		out.println("             final int calcindex = Nodes.getCalcIndex(nameWithParamcounter);");
		out.println("             if (calcindex>=0) {");
		out.println("                return Nodes.cmt(calcindex, this, args).stringValue();");
		out.println("             }");
		out.println("             /* input? */");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex>=0) {");
		out.println("                return ((V)I.dynamicCall(inputindex, -2, this, args)).stringValue();");
		out.println("             }");
		out.println("             /* function? */");
		out.println("             final int funcindex = F.getFuncIndex(nameUpper);");
		out.println("             if (funcindex>=0) {");
		out.println("                return F.dynamicCall(V.getInstanceFuncref(funcindex), this, args).stringValue();");
		out.println("             }");
		out.println("          } else {");
		out.println("             /* input-calc */");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex<0) {");
		out.println("                break;");
		out.println("             }");
		out.println("             final int inputcalcid = getInputCalcIndex(calcnameUppercase);");
		out.println("             if (inputcalcid<0) {");
		out.println("                break;");
		out.println("             }");
		out.println("             return ((V) I.dynamicCall(inputindex, inputcalcid, this, args)).stringValue();");
		out.println("          }");
		out.println("          break;");
		out.println("       }");
		out.println("       case 1: { //calc or function");
		out.println("          /* calc? */");
		out.println("          if (calcnameUppercase!=null) {");
		out.println("             break;");
		out.println("          }");
		out.println("          final int calcindex = Nodes.getCalcIndex(nameWithParamcounter);");
		out.println("          if (calcindex>=0) {");
		out.println("             return Nodes.cmt(calcindex, this, args).stringValue();");
		out.println("          }");
		out.println("          /* function? */");
		out.println("          final int funcindex = F.getFuncIndex(nameUpper);");
		out.println("          if (funcindex>=0) {");
		out.println("             return F.dynamicCall(V.getInstanceFuncref(funcindex), this, args).stringValue();");
		out.println("          }");
		out.println("          break;");
		out.println("       }");
		out.println("       case 2: { //input or table");
		out.println("          if (calcnameUppercase==null) {");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex>=0) {");
		out.println("                return ((V)I.dynamicCall(inputindex, -2, this, args)).stringValue();");
		out.println("             }");
		out.println("             final int tabindex = Tables.getTableid(nameUpper);");
		out.println("             if (tabindex>=0) {");
		out.println("                return ((V) Tables.dynamicCall(");
		out.println("                      V.getInstanceTabref(tabindex), ");
		out.println("                      Tables.CALL_EXACT, ");
		out.println("                      (Object[]) args");
		out.println("                      )).stringValue();");
		out.println("             }");
		out.println("             break;");
		out.println("          } else {");
		out.println("             final int inputindex = getInputIndex(nameUpper);");
		out.println("             if (inputindex>=0) {");
		out.println("                final int inputcalcid = getInputCalcIndex(calcnameUppercase);");
		out.println("                if (inputcalcid<0) {");
		out.println("                   break;");
		out.println("                }");
		out.println("                return ((V) I.dynamicCall(inputindex, inputcalcid, this, args)).stringValue();");
		out.println("             }");
		out.println("             final int tabindex = Tables.getTableid(nameUpper);");
		out.println("             if (tabindex>=0) {");
		out.println("                int lenvargs = args.length;");
		out.println("                V[] dynargs = new V[lenvargs+1];");
		out.println("                dynargs[0] = V.getInstance(calcnameUppercase);");
		out.println("                System.arraycopy(args, 0, dynargs, 1, lenvargs);");
		out.println("                return ((V) Tables.dynamicCall(");
		out.println("                      V.getInstanceTabref(tabindex), ");
		out.println("                      Tables.CALL_EXACT_COL,");
		out.println("                      (Object[]) dynargs");
		out.println("                      )).stringValue();");
		out.println("             }");
		out.println("          }");
		out.println("       } //end of case 2");
		out.println("       } //end of switch");
		out.println("       throw new RuntimeException(\"invalid calculation string '\" + name + \"'\");");
		out.println("   }");
		out.println("   @Override");
		out.println("   public String calculate(long id) {");
		out.println("      return calculate(id, (int[]) null);");
		out.println("   }");
		out.println("   @Override");
		out.println("   public String calculate(long id, String... args) {");
		out.println("      V[] vargs;");
		out.println("      if (args==null) {");
		out.println("         vargs = new V[0];");
		out.println("      } else {");
		out.println("         final int len = args.length;");
		out.println("         vargs = new V[args.length];");
		out.println("         for (int i=0; i<len; i++) {");
		out.println("            vargs[i] = V.getInstance(args[i]);");
		out.println("         }");
		out.println("      }");
		out.println("      int index = (int) (id>>32);");
		out.println("      int lowint  = (int) id;");
		out.println("      int whatitis = lowint & 0x7;");
		out.println("      switch (whatitis) {");
		out.println("      case 0: { //calcindex     000.....000: calc");
		out.println("         return Nodes.cmt(index, this, vargs).stringValue();");
		out.println("      }");
		out.println("      case 1: { // inputindex    000.....001: input");
		out.println("         return ((V) I.dynamicCall(index, -2, this, vargs)).stringValue();");
		out.println("      }");
		out.println("      case 2: { // inpid         calcid..010: input-calc");
		out.println("         final int calcid = lowint >> 3;");
		out.println("          return ((V) I.dynamicCall(index, calcid, this, vargs)).stringValue();");
		out.println("      }");
		out.println("      case 3:  { //funcid        000.....011: func");
		out.println("         return F.dynamicCall(V.getInstanceFuncref(index), this, vargs).stringValue();");
		out.println("      }");
		out.println("      case 4:  { //tabid         000.....100: table");
		out.println("         return ((V) Tables.dynamicCall(V.getInstanceTabref(index), Tables.CALL_EXACT, (Object[]) vargs)).stringValue();");
		out.println("      }");
		out.println("      case 5: { //tabid         colind..101: table-col");
		out.println("         final int colind = lowint >> 3;");
		out.println("             int lenvargs = vargs.length;");
		out.println("             Object[] dynargs = new Object[lenvargs+1];");
		out.println("             dynargs[0] = colind;");
		out.println("              System.arraycopy(vargs, 0, dynargs, 1, lenvargs);");
		out.println("              return ((V) Tables.dynamicCall(");
		out.println("                     V.getInstanceTabref(index), ");
		out.println("                      Tables.CALL_EXACT_COLIND,");
		out.println("                      dynargs");
		out.println("                      )).stringValue();");
		out.println("      }");
		out.println("      }");
		out.println("      throw new RuntimeException(\"invalid calculation id: \" + id);");
		out.println("   }");
		out.println("   @Override");
		out.println("   public String calculate(long id, int... args) {");
		out.println("      V[] vargs;");
		out.println("      if (args==null) {");
		out.println("         vargs = new V[0];");
		out.println("      } else {");
		out.println("         final int len = args.length;");
		out.println("         vargs = new V[args.length];");
		out.println("         for (int i=0; i<len; i++) {");
		out.println("            vargs[i] = V.getInstance(args[i]);");
		out.println("         }");
		out.println("      }");
		out.println("      int index = (int) (id>>32);");
		out.println("      int lowint  = (int) id;");
		out.println("      int whatitis = lowint & 0x7;");
		out.println("      switch (whatitis) {");
		out.println("      case 0: { //calcindex     000.....000: calc");
		out.println("         return Nodes.cmt(index, this, vargs).stringValue();");
		out.println("      }");
		out.println("      case 1: { // inputindex    000.....001: input");
		out.println("         return ((V) I.dynamicCall(index, -2, this, vargs)).stringValue();");
		out.println("      }");
		out.println("      case 2: { // inpid         calcid..010: input-calc");
		out.println("         final int calcid = lowint >> 3;");
		out.println("          return ((V) I.dynamicCall(index, calcid, this, vargs)).stringValue();");
		out.println("      }");
		out.println("      case 3:  { //funcid        000.....011: func");
		out.println("         return F.dynamicCall(V.getInstanceFuncref(index), this, vargs).stringValue();");
		out.println("      }");
		out.println("      case 4:  { //tabid         000.....100: table");
		out.println("         return ((V) Tables.dynamicCall(V.getInstanceTabref(index), Tables.CALL_EXACT, (Object[]) vargs)).stringValue();");
		out.println("      }");
		out.println("      case 5: { //tabid         colind..101: table-col");
		out.println("         final int colind = lowint >> 3;");
		out.println("             int lenvargs = vargs.length;");
		out.println("             Object[] dynargs = new Object[lenvargs+1];");
		out.println("             dynargs[0] = colind;");
		out.println("              System.arraycopy(vargs, 0, dynargs, 1, lenvargs);");
		out.println("              return ((V) Tables.dynamicCall(");
		out.println("                     V.getInstanceTabref(index), ");
		out.println("                      Tables.CALL_EXACT_COLIND,");
		out.println("                      dynargs");
		out.println("                      )).stringValue();");
		out.println("      }");
		out.println("      }");
		out.println("      throw new RuntimeException(\"invalid calculation id: \" + id);");
		out.println("   }");
		out.println("   @Override");
		out.println("   public boolean needed(String inputname, String calcname) {");
		out.println("      final Object[] parsedname = parseSetvarName(inputname);");
		out.println("      if (parsedname==null) {");
		out.println("         return false;");
		out.println("      }");
		out.println("      final String justnameUpper = (String) parsedname[0];");
		out.println("      int[] index = (int[]) parsedname[1];");
		out.println("      if (index==null) {");
		out.println("         index = new int[0];");
		out.println("      }");
		out.println("      final int inputid = getInputIndex(justnameUpper);");
		out.println("      if (inputid<0) {");
		out.println("         throw new RuntimeException(\"Invalid inputname \" + inputname);");
		out.println("      }");
		out.println("      int indexlen = index.length;");
		out.println("      V[] vindex = new V[indexlen];");
		out.println("      for (int i=0; i<indexlen; i++) {");
		out.println("         vindex[i] = V.getInstance(index[i]);");
		out.println("      }");
		out.println("      V value = getInput(inputid, vindex);");
		out.println("      setInput(inputid, null, index); // set to undefined");
		out.println("      boolean needed = false;");
		out.println("      try {");
		out.println("         calculate(calcname);");
		out.println("         needed = false;");
		out.println("      } catch (ExceptionNeedMoreInput e) {");
		out.println("         needed = true;");
		out.println("         String inputnameNeeded = e.getInputname();");
		out.println("         if (inputnameNeeded.equalsIgnoreCase(justnameUpper)) {");
		out.println("            int[] indexNeeded = e.getIndex();");
		out.println("            int neededLen = indexNeeded!=null ? indexNeeded.length : 0;");
		out.println("            if (neededLen==indexlen) {");
		out.println("               for (int i=0; i<indexlen; i++) {");
		out.println("                  if (indexNeeded[i]!=index[i]) {");
		out.println("                     needed = false;");
		out.println("                     break;");
		out.println("                  }");
		out.println("               }");
		out.println("            } else if (neededLen<indexlen) {");
		out.println("               for (int i=0; i<neededLen; i++) {");
		out.println("                  if (indexNeeded[i]!=index[i]) {");
		out.println("                     needed = false;");
		out.println("                     break;");
		out.println("                  }");
		out.println("               }");
		out.println("               for (int i=neededLen; i<indexlen; i++) {");
		out.println("                  if (index[i]!=0) {");
		out.println("                     needed = false;");
		out.println("                     break;");
		out.println("                  }");
		out.println("               }");
		out.println("            } else if (neededLen>indexlen) {");
		out.println("               for (int i=0; i<indexlen; i++) {");
		out.println("                  if (indexNeeded[i]!=index[i]) {");
		out.println("                     needed = false;");
		out.println("                     break;");
		out.println("                  }");
		out.println("               }");
		out.println("               for (int i=indexlen; i<neededLen; i++) {");
		out.println("                  if (indexNeeded[i]!=0) {");
		out.println("                     needed = false;");
		out.println("                     break;");
		out.println("                  }");
		out.println("               }");
		out.println("            }");
		out.println("         }");
		out.println("      }");
		out.println("      setInput(inputid, value, index); // set to original value");
		out.println("      return needed;");
		out.println("   }");

		if (!gwt) {
			out.println();
			out.println("   private HashMap<String,S> submodels = new HashMap<String,S>();");
			out.println("   public S getSubmodel(String vpmname) {");
			out.println("      S submodel = submodels.get(vpmname);");
			out.println("      if (submodel!=null) {");
			out.println("         return submodel;");
			out.println("      }");
			out.println("");
			out.println("      String modelname = new File(vpmname).getName().toLowerCase();");
			out.println("      int ind = modelname.lastIndexOf('.');");
			out.println("      if (ind>0) {");
			out.println("         modelname = modelname.substring(0, ind);");
			out.println("      }");
			out.println("      modelname = modelname.replaceAll(\"\\\\W\", \"_\");");
			out.println("      try {");
			out.println("         Class<?> myclass = Class.forName(\"gen.\" + modelname + \"._S\");");
			out.println("         Constructor<?> constr = myclass.getConstructor(new Class[0]);");
			out.println("         Object obj = constr.newInstance(new Object[0]);");
			out.println("         submodel = (S) obj;");
			out.println("         submodels.put(vpmname, submodel);");
			out.println("         return submodel;");
			out.println("      } catch (ClassNotFoundException e) {");
			out.println("      } catch (SecurityException e) {");
			out.println("      } catch (NoSuchMethodException e) {");
			out.println("      } catch (IllegalArgumentException e) {");
			out.println("      } catch (InstantiationException e) {");
			out.println("      } catch (IllegalAccessException e) {");
			out.println("      } catch (InvocationTargetException e) {");
			out.println("      }");
			out.println("      return null;");
			out.println("   }");
		} else {
			out.println();
			out.println("   public S getSubmodel(String vpmname) {");
			out.println("      return null;");
			out.println("   }");
		}
		out.println();
		out.println("   @Override");
		out.println("   public String toString() {");
		out.println("   	return cache.toString();");
		out.println("   }");

		if (trace) {
			out.println();
			out.println("   private int calllevel=0;");
			out.println("   private String traceLastmsg;");
			out.println("   private Traceaction traceLastaction;");
			out.println("   private PrintWriter pw;");
			out.println("   {");
			out.println("      try { ");
			out.println("         pw = new PrintWriter(new FileWriter(\"c:/temp/calls.txt\", true));");
			out.println("      } catch (Exception e) {");
			out.println("      }");
			out.println("   }");
			out.println("   @Override");
			out.println("   public void trace(String msg, String result, Traceaction traceaction) {");
			out.println("      int i;");
			out.println("      try { ");
			out.println("         switch(traceaction) {");
			out.println("         case MESSAGE: ");
			out.println("             if(traceLastaction==Traceaction.CALL) { pw.println(); }");
			out.println("             for (i=0; i<calllevel; i++) { pw.print(' '); }");
			out.println("             pw.print(msg);");
			out.println("             break;");
			out.println("         case CALL: ");
			out.println("             if(traceLastaction==Traceaction.CALL) { pw.println(); }");
			out.println("             for (i=0; i<calllevel; i++) { pw.print(' '); }");
			out.println("             pw.print(msg);");
			out.println("             calllevel++;");
			out.println("             break;");
			out.println("         case RETURN_SIMPLE: ");
			out.println("             if (traceLastaction==Traceaction.CALL && msg.equals(traceLastmsg)) {");
			out.println("                calllevel--;");
			out.println("                pw.print(\" -> \");");
			out.println("                pw.print(result);");
			out.println("                pw.println(\" {cache off}\");");
			out.println("             } else {");
			out.println("                calllevel--;");
			out.println("                if(traceLastaction==Traceaction.CALL) { pw.println(); }");
			out.println("                for (i=0; i<calllevel; i++) { pw.print(' '); }");
			out.println("                pw.print(msg);");
			out.println("                pw.print(\" -> \");");
			out.println("                pw.print(result);");
			out.println("                pw.println(\" {cache off}\");");
			out.println("             }");
			out.println("             break;");
			out.println("         case RETURN_MISS: ");
			out.println("             if (traceLastaction==Traceaction.CALL && msg.equals(traceLastmsg)) {");
			out.println("                calllevel--;");
			out.println("                pw.print(\" -> \");");
			out.println("                pw.print(result);");
			out.println("                pw.println(\" {cache off}\");");
			out.println("             } else {");
			out.println("                calllevel--;");
			out.println("                if(traceLastaction==Traceaction.CALL) { pw.println(); }");
			out.println("                for (i=0; i<calllevel; i++) { pw.print(' '); }");
			out.println("                pw.print(msg);");
			out.println("                pw.print(\" -> \");");
			out.println("                pw.print(result);");
			out.println("                pw.println(\" {cache miss}\");");
			out.println("             }");
			out.println("             break;");
			out.println("         case RETURN_HIT: ");
			out.println("             //calllevel--;");
			out.println("             if(traceLastaction==Traceaction.CALL) { pw.println(); }");
			out.println("             for (i=0; i<calllevel; i++) { pw.print(' '); }");
			out.println("             pw.print(msg);");
			out.println("             pw.print(\" -> \");");
			out.println("             pw.print(result);");
			out.println("             pw.println(\" {cache hit}\");");
			out.println("             break;");
			out.println("         }");
			out.println("      } catch (Exception e) {");
			out.println("      }");
			out.println("      traceLastaction = traceaction;");
			out.println("      traceLastmsg = msg;");
			out.println("      pw.flush();");
			out.println("   }");
		} else { //trace off
			out.println();
			out.println("   @Override");
			out.println("   public void trace(String msg, String result, Traceaction traceaction) {");
			out.println("   }");
		}
		
		out.println("}");

		out.close();
	}

	public static void generate(ModelSimple model, String foldernameout, String packagename, boolean gwt, boolean trace) throws IOException {
		JavaDiv obj = new JavaDiv(model, foldernameout, packagename, gwt, trace);
		obj.run();
	}
}
